Converting bitmaps to drawings

This notebook contains some simple code to convert bitmaps to line drawings. It uses OpenCV, mostly Canny for edge detection and findcontours to convert the edges into lines.

Part of getting this to work is trying various parameters and one way of doing that, is by running a whole set of them and showing the output of them in a grid. In this case we have how much we blur the image before edge detection and how much we simplify lines.

This made me think of Warhol's famous painting of Marilyn Monroe so it seemed interesting to make this notebook into an ode to that


In [23]:
%matplotlib inline
import cv2
from skimage import io
from matplotlib import pyplot as plt
import matplotlib as pylab
pylab.rcParams['figure.figsize'] = 8, 8
import numpy as np
import requests

In [25]:
def find_edges(img, threshold=4, blur=10):
  blurred = cv2.bilateralFilter(img, blur, blur * 2, blur * 2)
  return cv2.Canny(blurred, threshold * 3, threshold * 20)

image = io.imread('http://oi64.tinypic.com/qs48r6.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

edged = find_edges(gray, threshold=4, blur=10)
plt.imshow(edged, cmap="gray")
plt.show()



In [27]:
def simplify(cnt, epsilon=0.005):
  return cv2.approxPolyDP(cnt, epsilon * cv2.arcLength(cnt, False), False)

def find_contours(img, epsilon):
  image, contours, hierarchy = cv2.findContours(img.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
  simplified = [simplify(cnt, epsilon) for cnt in contours]
  longest = sorted(simplified, key = lambda cnt:cv2.arcLength(cnt, False), reverse = True)
  return longest

longest = find_contours(edged, epsilon=0.01)
height, width = edged.shape
countour_im = np.zeros((height, width, 3), np.uint8)
cv2.drawContours(countour_im, longest[:50], -1, (200,200,200), 10)
plt.imshow(countour_im, cmap="gray")
plt.show()



In [30]:
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
height, width = gray.shape

padding = 20
epsilons = [0.04 ** (1 + i * 0.5) for i in range(4)]
blurs = [3, 6, 9, 12]

main_image = np.zeros((padding + (height + padding) * len(blurs), 
                       padding + (width + padding) * len(epsilons), 3), np.uint8)

for x, epsilon in enumerate(epsilons):
  for y, blur in enumerate(blurs):
    edged = find_edges(gray, threshold=10, blur=blur)
    longest = find_contours(edged, epsilon=epsilon)
    height, width = edged.shape
    contour_im = np.zeros((height, width, 3), np.uint8)
    contour_im[:] = (120 + 40 * x, 160, 140 + 30 * y)
    cv2.drawContours(contour_im, longest[:90], -1, (70, 70, 70), 10)
    yc = padding + y * (height + padding)
    xc = padding + x * (width + padding)
    main_image[yc: yc + height, xc: xc + width] = contour_im


plt.imshow(main_image)
plt.show()



In [31]:
cv2.imwrite('tmp.jpg', image)


Out[31]:
True

In [ ]: